﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MLSharp
{
	/// <summary>
	/// Contains the definition of a rule from a <see cref="IRuleSet"/>.
	/// </summary>
	public class Rule
	{
		#region Public Properties

		/// <summary>
		/// The number of cases covered by the rule.
		/// </summary>
		public virtual int Cover { get; set; }

		/// <summary>
		/// The number of cases that are correctly classified by the rule.
		/// </summary>
		public virtual int Correct { get; set; }

		/// <summary>
		/// Measure of the utility of the rule, calculated by dividing the 
		/// rule's estimated accuracy by the relative frequency of the 
		/// predicted class in the training data.
		/// </summary>
		public double Lift { get; set; }

		/// <summary>
		/// The class predicted by the rule.
		/// </summary>
		public string Class { get; set; }

		/// <summary>
		/// The conditions of the rule.
		/// </summary>
		public virtual RuleCondition[] Conditions { get; set; }

		#endregion

		#region Protected Methods

		/// <summary>
		/// Writes stats about the rule to the string builder, which is later used by <see cref="ToString()"/>
		/// </summary>
		/// <param name="sb"></param>
		protected virtual void WriteRuleStats(StringBuilder sb)
		{
			sb.AppendFormat("(Class=\"{0}\" Correct=\"{1}\" Cover=\"{2}\" Lift=\"{3}\")",
							Class,
							Correct,
							Cover,
							Lift);
		}

		#endregion

		#region Public Methods

		/// <summary>
		/// Returns a human-readable version of the rule.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			StringBuilder sb = new StringBuilder();

			WriteRuleStats(sb);

			sb.Append("{");

			for (int i=0; i < Conditions.Length; i++)
			{
				if (i > 0)
				{
					sb.Append(" && ");
				}

				sb.Append(Conditions[i].ToString());
			}

			sb.Append("}");

			return sb.ToString();
		}

		/// <summary>
		/// Rules are considered equal if they have equivalent conditions. 
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public override bool Equals(object obj)
		{
			Rule rule = obj as Rule;

			if (rule == null)
			{
				return false;
			}

			//If both rules have null conditions, they are equal.
			if (rule.Conditions == null || this.Conditions == null)
			{
				return rule.Conditions == this.Conditions;
			}

			if (rule.Conditions.Length != this.Conditions.Length)
			{
				return false;
			}

			foreach (RuleCondition condition in this.Conditions)
			{
				if (Array.IndexOf(rule.Conditions, condition) < 0)
				{
					return false;
				}
			}

			return true;
		}

		/// <summary>
		/// Gets a hashcode derived from the conditions of the rule.
		/// </summary>
		/// <returns></returns>
		public override int GetHashCode()
		{
			int hashCode = 0;

			if (Conditions != null)
			{
				foreach (RuleCondition condition in Conditions)
				{
					hashCode ^= condition.GetHashCode();
				}
			}

			return hashCode;
		}

		#endregion
	}
}
